Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00%
0 / 1
33.33%
8 / 24
CRAP
78.67%
439 / 558
Config
0.00%
0 / 1
33.33%
8 / 24
463.29
78.67%
439 / 558
 __construct(Application $app)
100.00%
1 / 1
1
100.00%
5 / 5
 initialize()
100.00%
1 / 1
2
100.00%
10 / 10
 set($path, $value)
0.00%
0 / 1
4.16
78.57%
11 / 14
 get($path, $default = null)
0.00%
0 / 1
6.01
93.33%
14 / 15
 getConfig()
100.00%
1 / 1
1
100.00%
12 / 12
 parseGeneral()
0.00%
0 / 1
16.40
60.00%
18 / 30
 parseTaxonomy()
0.00%
0 / 1
14.53
86.11%
31 / 36
 parseContentTypes($acceptableFileTypes)
100.00%
1 / 1
2
100.00%
7 / 7
 parseContentType($key, $contentType, $acceptableFileTypes)
0.00%
0 / 1
26.25
75.00%
33 / 44
 parseFieldsAndGroups($fields, $acceptableFileTypes)
0.00%
0 / 1
14.21
89.74%
35 / 39
 parseConfigYaml($filename, $path = null)
100.00%
1 / 1
5
100.00%
9 / 9
 checkConfig()
0.00%
0 / 1
105.80
57.30%
51 / 89
 getFields()
100.00%
1 / 1
1
100.00%
2 / 2
 setDefaults()
100.00%
1 / 1
2
100.00%
67 / 67
 setTwigPath()
0.00%
0 / 1
4.59
66.67%
8 / 12
 setCKPath()
100.00%
1 / 1
1
100.00%
13 / 13
 loadCache()
0.00%
0 / 1
14.15
90.91%
20 / 22
 saveCache()
0.00%
0 / 1
2.15
66.67%
4 / 6
 checkValidCache()
0.00%
0 / 1
3.21
71.43%
5 / 7
 getDBOptions()
0.00%
0 / 1
23.32
79.76%
67 / 84
 getWhichEnd($mountpoint = '')
0.00%
0 / 1
12.42
77.27%
17 / 22
 getTimestamp($when)
0.00%
0 / 1
2
0.00%
0 / 3
 getCurrentTimestamp()
0.00%
0 / 1
2
0.00%
0 / 3
 flashError($error)
0.00%
0 / 1
12
0.00%
0 / 7
<?php
namespace Bolt;
use Bolt\Configuration\LowlevelException;
use Bolt\Library as Lib;
use Bolt\Helpers\Arr;
use Bolt\Helpers\String;
use Bolt\Translation\Translator as Trans;
use Symfony\Component\Yaml;
use Symfony\Component\Yaml\Parser;
/**
* Class for our config object.
*
* @author Bob den Otter, bob@twokings.nl
*/
class Config
{
protected $paths;
protected $app;
/** @var Configuration\ResourceManager */
protected $resources;
protected $data;
protected $defaultConfig = array();
protected $reservedFieldNames = array(
'id', 'slug', 'datecreated', 'datechanged', 'datepublish', 'datedepublish', 'ownerid', 'username', 'status', 'link'
);
protected $cachetimestamp;
/**
* Use {@see Config::getFields} instead.
* Will be made protected in Bolt 3.0.
* @var Field\Manager
*/
public $fields;
protected $yamlParser = false;
/**
* @param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
$this->resources = $app['resources'];
$this->fields = new Field\Manager();
$this->initialize();
}
protected function initialize()
{
$this->defaultConfig = $this->setDefaults();
if (!$this->loadCache()) {
$this->data = $this->getConfig();
$this->saveCache();
// if we have to reload the config, we will also want to make sure the DB integrity is checked.
Database\IntegrityChecker::invalidate($this->app);
} else {
// In this case the cache is loaded, but because the path of the theme
// folder is defined in the config file itself, we still need to check
// retrospectively if we need to invalidate it.
$this->checkValidCache();
}
$this->data['twigpath'] = $this->setTwigPath();
$this->setCKPath();
}
/**
* Set a config value, using a path. For example:
*
* $app['config']->set('general/branding/name', 'Bolt');
*
* @param string $path
* @param mixed $value
* @return bool
*/
public function set($path, $value)
{
$path = explode('/', $path);
// Only do something if we get at least one key.
if (empty($path[0])) {
$logline = "Config: can't set empty path to '" . (string) $value . "'";
$this->app['log']->add($logline, 3, '', 'config');
return false;
}
$part = & $this->data;
foreach ($path as $key) {
if (!isset($part[$key])) {
$part[$key] = array();
}
$part = & $part[$key];
}
$part = $value;
return true;
}
/**
* Get a config value, using a path. For example:
*
* $var = $config->get('general/wysiwyg/ck/contentsCss');
*
* @param string $path
* @param string $default
* @return mixed
*/
public function get($path, $default = null)
{
$path = explode('/', $path);
// Only do something if we get at least one key.
if (empty($path[0]) || !isset($this->data[$path[0]])) {
return false;
}
$part = & $this->data;
$value = null;
foreach ($path as $key) {
if (!isset($part[$key])) {
$value = null;
break;
}
$value = $part[$key];
$part = & $part[$key];
}
if ($value !== null) {
return $value;
}
return $default;
}
/**
* Load the configuration from the various YML files.
*/
public function getConfig()
{
$config = array();
$config['general'] = $this->parseGeneral();
$config['taxonomy'] = $this->parseTaxonomy();
$config['contenttypes'] = $this->parseContentTypes($config['general']['accept_file_types']);
$config['menu'] = $this->parseConfigYaml('menu.yml');
$config['routing'] = $this->parseConfigYaml('routing.yml');
$config['permissions'] = $this->parseConfigYaml('permissions.yml');
$config['extensions'] = array();
$this->resources->initializeConfig($config);
$config['theme'] = $this->parseConfigYaml('config.yml', $this->resources->getPath('theme'));
// @todo: If no config files can be found, get them from bolt.cm/files/default/
$this->paths = $this->resources->getPaths();
return $config;
}
protected function parseGeneral()
{
// Read the config and merge it. (note: We use temp variables to prevent
// "Only variables should be passed by reference")
$tempconfig = $this->parseConfigYaml('config.yml');
$tempconfiglocal = $this->parseConfigYaml('config_local.yml');
$general = Arr::mergeRecursiveDistinct($tempconfig, $tempconfiglocal);
// Make sure old settings for 'contentsCss' are still picked up correctly
if (isset($general['wysiwyg']['ck']['contentsCss'])) {
$general['wysiwyg']['ck']['contentsCss'] = array(
1 => $general['wysiwyg']['ck']['contentsCss']
);
}
// Make sure old settings for 'accept_file_types' are not still picked up. Before 1.5.4 we used to store them
// as a regex-like string, and we switched to an array. If we find the old style, fall back to the defaults.
if (isset($general['accept_file_types']) && !is_array($general['accept_file_types'])) {
unset($general['accept_file_types']);
}
// Merge the array with the defaults. Setting the required values that aren't already set.
$general = Arr::mergeRecursiveDistinct($this->defaultConfig, $general);
// Make sure the cookie_domain for the sessions is set properly.
if (empty($general['cookies_domain'])) {
if (isset($_SERVER['HTTP_HOST'])) {
$hostname = $_SERVER['HTTP_HOST'];
} elseif (isset($_SERVER['SERVER_NAME'])) {
$hostname = $_SERVER['SERVER_NAME'];
} else {
$hostname = '';
}
// Don't set the domain for a cookie on a "TLD" - like 'localhost', or if the server_name is an IP-address
if ((strpos($hostname, '.') > 0) && preg_match("/[a-z0-9]/i", $hostname)) {
if (preg_match("/^www[0-9]*./", $hostname)) {
$general['cookies_domain'] = '.' . preg_replace("/^www[0-9]*./", '', $hostname);
} else {
$general['cookies_domain'] = '.' . $hostname;
}
// Make sure we don't have consecutive '.'-s in the cookies_domain..
$general['cookies_domain'] = str_replace('..', '.', $general['cookies_domain']);
} else {
$general['cookies_domain'] = '';
}
}
// Make sure Bolt's mount point is OK:
$general['branding']['path'] = '/' . String::makeSafe($general['branding']['path']);
return $general;
}
protected function parseTaxonomy()
{
$taxonomies = $this->parseConfigYaml('taxonomy.yml');
foreach ($taxonomies as $key => $taxonomy) {
if (!isset($taxonomy['name'])) {
$taxonomy['name'] = ucwords($taxonomy['slug']);
}
if (!isset($taxonomy['singular_name'])) {
if (isset($taxonomy['singular_slug'])) {
$taxonomy['singular_name'] = ucwords($taxonomy['singular_slug']);
} else {
$taxonomy['singular_name'] = ucwords($taxonomy['slug']);
}
}
if (!isset($taxonomy['slug'])) {
$taxonomy['slug'] = strtolower(String::makeSafe($taxonomy['name']));
}
if (!isset($taxonomy['singular_slug'])) {
$taxonomy['singular_slug'] = strtolower(String::makeSafe($taxonomy['singular_name']));
}
if (!isset($taxonomy['has_sortorder'])) {
$taxonomy['has_sortorder'] = false;
}
// Make sure the options are $key => $value pairs, and not have implied integers for keys.
if (!empty($taxonomy['options']) && is_array($taxonomy['options'])) {
$options = array();
foreach ($taxonomy['options'] as $optionkey => $optionvalue) {
if (is_numeric($optionkey)) {
$optionkey = String::slug($optionvalue);
}
$options[$optionkey] = $optionvalue;
}
$taxonomy['options'] = $options;
}
// If taxonomy is like tags, set 'tagcloud' to true by default.
if (($taxonomy['behaves_like'] == 'tags') && (!isset($taxonomy['tagcloud']))) {
$taxonomy['tagcloud'] = true;
}
$taxonomies[$key] = $taxonomy;
}
return $taxonomies;
}
protected function parseContentTypes($acceptableFileTypes)
{
$contentTypes = array();
$tempContentTypes = $this->parseConfigYaml('contenttypes.yml');
foreach ($tempContentTypes as $key => $contentType) {
$contentType = $this->parseContentType($key, $contentType, $acceptableFileTypes);
$contentTypes[$contentType['slug']] = $contentType;
}
return $contentTypes;
}
protected function parseContentType($key, $contentType, $acceptableFileTypes)
{
// If the slug isn't set, and the 'key' isn't numeric, use that as the slug.
if (!isset($contentType['slug']) && !is_numeric($key)) {
$contentType['slug'] = String::slug($key);
}
// If neither 'name' nor 'slug' is set, we need to warn the user. Same goes for when
// neither 'singular_name' nor 'singular_slug' is set.
if (!isset($contentType['name']) && !isset($contentType['slug'])) {
$error = sprintf("In contenttype <code>%s</code>, neither 'name' nor 'slug' is set. Please edit <code>contenttypes.yml</code>, and correct this.", $key);
throw new LowlevelException($error);
}
if (!isset($contentType['singular_name']) && !isset($contentType['singular_slug'])) {
$error = sprintf("In contenttype <code>%s</code>, neither 'singular_name' nor 'singular_slug' is set. Please edit <code>contenttypes.yml</code>, and correct this.", $key);
throw new LowlevelException($error);
}
if (!isset($contentType['slug'])) {
$contentType['slug'] = String::slug($contentType['name']);
}
if (!isset($contentType['singular_slug'])) {
$contentType['singular_slug'] = String::slug($contentType['singular_name']);
}
if (!isset($contentType['show_on_dashboard'])) {
$contentType['show_on_dashboard'] = true;
}
if (!isset($contentType['show_in_menu'])) {
$contentType['show_in_menu'] = true;
}
if (!isset($contentType['sort'])) {
$contentType['sort'] = false;
}
if (!isset($contentType['default_status'])) {
$contentType['default_status'] = 'draft';
}
list($fields, $groups) = $this->parseFieldsAndGroups($contentType['fields'], $acceptableFileTypes);
$contentType['fields'] = $fields;
if (!empty($groups)) {
$contentType['groups'] = $groups;
}
// Make sure taxonomy is an array.
if (isset($contentType['taxonomy']) && !is_array($contentType['taxonomy'])) {
$contentType['taxonomy'] = array($contentType['taxonomy']);
}
// when adding relations, make sure they're added by their slug. Not their 'name' or 'singular name'.
if (!empty($contentType['relations']) && is_array($contentType['relations'])) {
foreach ($contentType['relations'] as $relkey => $relation) {
if ($relkey != String::slug($relkey)) {
$contentType['relations'][String::slug($relkey)] = $contentType['relations'][$relkey];
unset($contentType['relations'][$relkey]);
}
}
}
return $contentType;
}
protected function parseFieldsAndGroups($fields, $acceptableFileTypes)
{
$currentGroup = false;
$groups = array();
foreach ($fields as $key => $field) {
unset($fields[$key]);
$key = str_replace('-', '_', strtolower(String::makeSafe($key, true)));
// If field is a "file" type, make sure the 'extensions' are set, and it's an array.
if ($field['type'] == 'file' || $field['type'] == 'filelist') {
if (empty($field['extensions'])) {
$field['extensions'] = array_intersect(
array('doc', 'docx', 'txt', 'md', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'csv'),
$acceptableFileTypes
);
}
if (!is_array($field['extensions'])) {
$field['extensions'] = array($field['extensions']);
}
}
// If field is an "image" type, make sure the 'extensions' are set, and it's an array.
if ($field['type'] == 'image' || $field['type'] == 'imagelist') {
if (empty($field['extensions'])) {
$field['extensions'] = array_intersect(
array('gif', 'jpg', 'jpeg', 'png'),
$acceptableFileTypes
);
}
if (!is_array($field['extensions'])) {
$field['extensions'] = array($field['extensions']);
}
}
// If the field has a 'group', make sure it's added to the 'groups' array, so we can turn
// them into tabs while rendering. This also makes sure that once you started with a group,
// all others have a group too.
if (!empty($field['group'])) {
$currentGroup = $field['group'];
$groups[] = $currentGroup;
} else {
$field['group'] = $currentGroup;
}
$fields[$key] = $field;
}
// Make sure the 'uses' of the slug is an array.
if (isset($fields['slug']) && isset($fields['slug']['uses']) &&
!is_array($fields['slug']['uses'])
) {
$fields['slug']['uses'] = array($fields['slug']['uses']);
}
$groups = array_unique($groups);
return array($fields, $groups);
}
/**
* @param string $filename The name of the YAML file to read
* @param string $path The (optional) path to the YAML file
* @return array
*/
protected function parseConfigYaml($filename, $path = null)
{
// Initialise parser
if ($this->yamlParser === false) {
$this->yamlParser = new Parser();
}
// By default we assume that config files are located in app/config/
$path = $path ?: $this->resources->getPath('config');
$filename = $path . '/' . $filename;
if (!is_readable($filename)) {
return array();
}
$yml = $this->yamlParser->parse(file_get_contents($filename) . "\n");
// Invalid, non-existing, or empty files return NULL
return $yml ?: array();
}
/**
* Sanity checks for doubles in in contenttypes.
*/
public function checkConfig()
{
$slugs = array();
$wrongctype = false;
foreach ($this->data['contenttypes'] as $key => $ct) {
/**
* Make sure any field that has a 'uses' parameter actually points to a field that exists.
* For example, this will show a notice:
* entries:
* name: Entries
* singular_name: Entry
* fields:
* title:
* type: text
* class: large
* slug:
* type: slug
* uses: name
*/
foreach ($ct['fields'] as $fieldname => $field) {
// Verify that the contenttype doesn't try to add fields that are reserved.
if ($fieldname != 'slug' && in_array($fieldname, $this->reservedFieldNames)) {
$error = Trans::__(
'contenttypes.generic.reserved-name',
array('%contenttype%' => $key, '%field%' => $fieldname)
);
$this->flashError($error);
return;
}
// Check 'uses'. If it's an array, split it up, and check the separate parts. We also need to check
// for the fields that are always present, like 'id'.
if (is_array($field) && !empty($field['uses'])) {
foreach ($field['uses'] as $useField) {
if (!empty($field['uses']) && empty($ct['fields'][$useField]) && !in_array($useField, $this->reservedFieldNames)) {
$error = Trans::__(
'contenttypes.generic.wrong-use-field',
array('%contenttype%' => $key, '%field%' => $fieldname, '%uses%' => $useField)
);
$this->flashError($error);
return;
}
}
}
// Make sure we have a 'label', 'class', 'variant' and 'default'.
if (!isset($field['label'])) {
$this->set("contenttypes/{$key}/fields/{$fieldname}/label", '');
}
if (!isset($field['class'])) {
$this->set("contenttypes/{$key}/fields/{$fieldname}/class", 'form-control');
} else {
$this->set("contenttypes/{$key}/fields/{$fieldname}/class", 'form-control ' . $field['class']);
}
if (!isset($field['variant'])) {
$this->set("contenttypes/{$key}/fields/{$fieldname}/variant", '');
}
if (!isset($field['default'])) {
$this->set("contenttypes/{$key}/fields/{$fieldname}/default", '');
}
if (!isset($field['pattern'])) {
$this->set("contenttypes/{$key}/fields/{$fieldname}/pattern", '');
}
// Make sure the 'type' is in the list of allowed types
if (!isset($field['type']) || !$this->fields->has($field['type'])) {
$error = Trans::__(
'contenttypes.generic.no-proper-type',
array('%contenttype%' => $key, '%field%' => $fieldname, '%type%' =>
$field['type'])
);
$this->flashError($error);
$wrongctype = true && $this->app['users']->getCurrentUsername();
}
}
// Keep a running score of used slugs..
if (!isset($slugs[$ct['slug']])) {
$slugs[$ct['slug']] = 0;
}
$slugs[$ct['slug']]++;
if (!isset($slugs[$ct['singular_slug']])) {
$slugs[$ct['singular_slug']] = 0;
}
if ($ct['singular_slug'] != $ct['slug']) {
$slugs[$ct['singular_slug']]++;
}
}
// Check DB-tables integrity
if (!$wrongctype && $this->app['integritychecker']->needsCheck() &&
(count($this->app['integritychecker']->checkTablesIntegrity()) > 0) &&
$this->app['users']->getCurrentUsername()) {
$msg = Trans::__(
"The database needs to be updated/repaired. Go to 'Settings' > '<a href=\"%link%\">Check Database</a>' to do this now.",
array('%link%' => Lib::path('dbcheck'))
);
$this->flashError($msg);
return;
}
// Sanity checks for taxonomy.yml
foreach ($this->data['taxonomy'] as $key => $taxo) {
// Show some helpful warnings if slugs or keys are not set correctly.
if ($taxo['slug'] != $key) {
$error = Trans::__(
"The identifier and slug for '%taxonomytype%' are the not the same ('%slug%' vs. '%taxonomytype%'). Please edit taxonomy.yml, and make them match to prevent inconsistencies between database storage and your templates.",
array('%taxonomytype%' => $key, '%slug%' => $taxo['slug'])
);
$this->flashError($error);
return;
}
}
// if there aren't any other errors, check for duplicates across contenttypes..
if (!$this->app['session']->getFlashBag()->has('error')) {
foreach ($slugs as $slug => $count) {
if ($count > 1) {
$error = Trans::__(
"The slug '%slug%' is used in more than one contenttype. Please edit contenttypes.yml, and make them distinct.",
array('%slug%' => $slug)
);
$this->flashError($error);
return;
}
}
}
}
/**
* A getter to access the fields manager
*
* @return Field\Manager
**/
public function getFields()
{
return $this->fields;
}
/**
* Assume sensible defaults for a number of options.
*/
protected function setDefaults()
{
return array(
'database' => array('prefix' => 'bolt_'),
'sitename' => 'Default Bolt site',
'homepage' => 'page/*',
'homepage_template' => 'index.twig',
'locale' => \Bolt\Application::DEFAULT_LOCALE,
'recordsperpage' => 10,
'recordsperdashboardwidget' => 5,
'debug' => false,
'debug_show_loggedoff' => false,
'debug_error_level' => 6135,
// equivalent to E_ALL &~ E_NOTICE &~ E_DEPRECATED &~ E_USER_DEPRECATED
'debug_enable_whoops' => true,
'debug_permission_audit_mode' => false,
'frontend_permission_checks' => false,
'strict_variables' => false,
'theme' => 'default',
'debug_compressjs' => true,
'debug_compresscss' => true,
'listing_template' => 'listing.twig',
'listing_records' => '5',
'listing_sort' => 'datepublish DESC',
'caching' => array(
'config' => true,
'rendering' => false,
'templates' => false,
'request' => false
),
'wysiwyg' => array(
'images' => false,
'tables' => false,
'fontcolor' => false,
'align' => false,
'subsuper' => false,
'embed' => true,
'anchor' => false,
'underline' => false,
'strike' => false,
'blockquote' => true,
'codesnippet' => false,
'specialchar' => false,
'ck' => array(
'allowedContent' => true,
'autoParagraph' => true,
'contentsCss' => array(
$this->paths['app'] . 'view/lib/ckeditor/contents.css',
$this->paths['app'] . 'view/css/ckeditor.css',
),
'filebrowserWindowWidth' => 640,
'filebrowserWindowHeight' => 480
),
'filebrowser' => array(
'browseUrl' => $this->paths['async'] . 'filebrowser/',
'imageBrowseUrl' => $this->paths['bolt'] . 'files/files'
),
),
'canonical' => !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '',
'developer_notices' => false,
'cookies_use_remoteaddr' => true,
'cookies_use_browseragent' => false,
'cookies_use_httphost' => true,
'cookies_https_only' => false,
'cookies_lifetime' => 14 * 24 * 3600,
'thumbnails' => array(
'default_thumbnail' => array(160, 120),
'default_image' => array(1000, 750),
'quality' => 75,
'cropping' => 'crop',
'notfound_image' => 'view/img/default_notfound.png',
'error_image' => 'view/img/default_error.png'
),
'accept_file_types' => explode(",", "twig,html,js,css,scss,gif,jpg,jpeg,png,ico,zip,tgz,txt,md,doc,docx,pdf,epub,xls,xlsx,csv,ppt,pptx,mp3,ogg,wav,m4a,mp4,m4v,ogv,wmv,avi,webm,svg"),
'hash_strength' => 10,
'branding' => array(
'name' => 'Bolt',
'path' => '/bolt',
'provided_by' => array()
),
'maintenance_mode' => false
);
}
protected function setTwigPath()
{
$themepath = $this->resources->getPath("theme");
$end = $this->getWhichEnd($this->get('general/branding/path'));
if ($end == 'frontend' && file_exists($themepath)) {
$twigpath = array($themepath);
} else {
$twigpath = array(realpath($this->resources->getPath('app') . '/view/twig'));
}
if (!file_exists($themepath)) {
$error = "Template folder 'theme/" . basename($this->get('general/theme')) . "' does not exist, or is not writable.";
$this->flashError($error);
}
// We add these later, because the order is important: By having theme/ourtheme first,
// files in that folder will take precedence. For instance when overriding the menu template.
$twigpath[] = realpath($this->resources->getPath('app') . '/theme_defaults');
return $twigpath;
}
/**
* Will be made protected in Bolt 3.0
*/
public function setCKPath()
{
$this->paths = $this->resources->getPaths();
// Make sure the paths for CKeditor config are always set correctly..
$this->set(
'general/wysiwyg/ck/contentsCss',
array(
$this->paths['app'] . 'view/lib/ckeditor/contents.css',
$this->paths['app'] . 'view/css/ckeditor.css'
)
);
$this->set('general/wysiwyg/filebrowser/browseUrl', $this->resources->getUrl('async') . 'filebrowser/');
$this->set(
'general/wysiwyg/filebrowser/imageBrowseUrl',
$this->resources->getUrl('bolt') . 'files/files/'
);
}
protected function loadCache()
{
$dir = $this->resources->getPath('config');
/* Get the timestamps for the config files. config_local defaults to '0', because if it isn't present,
it shouldn't trigger an update for the cache, while the others should.
*/
$timestamps = array(
file_exists($dir . '/config.yml') ? filemtime($dir . '/config.yml') : 10000000000,
file_exists($dir . '/taxonomy.yml') ? filemtime($dir . '/taxonomy.yml') : 10000000000,
file_exists($dir . '/contenttypes.yml') ? filemtime($dir . '/contenttypes.yml') : 10000000000,
file_exists($dir . '/menu.yml') ? filemtime($dir . '/menu.yml') : 10000000000,
file_exists($dir . '/routing.yml') ? filemtime($dir . '/routing.yml') : 10000000000,
file_exists($dir . '/permissions.yml') ? filemtime($dir . '/permissions.yml') : 10000000000,
file_exists($dir . '/config_local.yml') ? filemtime($dir . '/config_local.yml') : 0,
);
if (file_exists($this->resources->getPath('cache') . '/config_cache.php')) {
$this->cachetimestamp = filemtime($this->resources->getPath('cache') . '/config_cache.php');
} else {
$this->cachetimestamp = 0;
}
if ($this->cachetimestamp > max($timestamps)) {
$this->data = Lib::loadSerialize($this->resources->getPath('cache') . '/config_cache.php');
// Check if we loaded actual data.
if (count($this->data) < 4 || empty($this->data['general'])) {
return false;
}
// Check to make sure the version is still the same. If not, effectively invalidate the
// cached config to force a reload.
if (!isset($this->data['version']) || ($this->data['version'] != $this->app->getVersion())) {
return false;
}
// Trigger the config loaded event on the resource manager
$this->resources->initializeConfig($this->data);
// Yup, all seems to be right.
return true;
}
return false;
}
protected function saveCache()
{
// Store the version number along with the config.
$this->data['version'] = $this->app->getVersion();
if ($this->get('general/caching/config')) {
Lib::saveSerialize($this->resources->getPath('cache') . '/config_cache.php', $this->data);
return;
}
@unlink($this->resources->getPath('cache') . '/config_cache.php');
}
protected function checkValidCache()
{
// Check the timestamp for the theme's config.yml
$paths = $this->resources->getPaths();
$themeConfigFile = $paths['themepath'] . '/config.yml';
// Note: we need to check if it exists, _and_ it's too old. Not _or_, hence the '0'
$configTimestamp = file_exists($themeConfigFile) ? filemtime($themeConfigFile) : 0;
if ($this->cachetimestamp <= $configTimestamp) {
// Invalidate cache for next request.
@unlink($paths['cache'] . '/config_cache.php');
}
}
/**
* Get an associative array with the correct options for the chosen database type.
*
* @return array
*/
public function getDBOptions()
{
$configdb = $this->data['general']['database'];
if (isset($configdb['driver']) && in_array($configdb['driver'], array('pdo_sqlite', 'sqlite'))) {
$basename = isset($configdb['databasename']) ? basename($configdb['databasename']) : 'bolt';
if (Lib::getExtension($basename) != 'db') {
$basename .= '.db';
}
if (isset($configdb["path"])) {
$configpaths = $this->resources->getPaths();
if (substr($configdb['path'], 0, 1) !== "/") {
$configdb['path'] = $configpaths["rootpath"] . '/' . $configdb['path'];
}
}
$dboptions = array(
'driver' => 'pdo_sqlite',
'path' => (isset($configdb['path']) ? realpath($configdb['path']) : $this->resources->getPath('database')) . '/' . $basename,
'randomfunction' => 'RANDOM()',
'memory' => isset($configdb['memory']) ? true : false
);
} else {
// Assume we configured it correctly. Yeehaa!
if (empty($configdb['password'])) {
$configdb['password'] = '';
}
$driver = (isset($configdb['driver']) ? $configdb['driver'] : 'pdo_mysql');
$randomfunction = '';
if (in_array($driver, array('mysql', 'mysqli'))) {
$driver = 'pdo_mysql';
$randomfunction = 'RAND()';
}
if (in_array($driver, array('postgres', 'postgresql'))) {
$driver = 'pdo_pgsql';
$randomfunction = 'RANDOM()';
}
$dboptions = array(
'driver' => $driver,
'host' => (isset($configdb['host']) ? $configdb['host'] : 'localhost'),
'dbname' => $configdb['databasename'],
'user' => $configdb['username'],
'password' => $configdb['password'],
'randomfunction' => $randomfunction
);
$dboptions['charset'] = isset($configdb['charset']) ? $configdb['charset'] : 'utf8';
}
switch ($dboptions['driver']) {
case 'pdo_mysql':
$dboptions['port'] = isset($configdb['port']) ? $configdb['port'] : '3306';
$dboptions['reservedwords'] = explode(
',',
'accessible,add,all,alter,analyze,and,as,asc,asensitive,before,between,' .
'bigint,binary,blob,both,by,call,cascade,case,change,char,character,check,collate,column,condition,constraint,' .
'continue,convert,create,cross,current_date,current_time,current_timestamp,current_user,cursor,database,databases,' .
'day_hour,day_microsecond,day_minute,day_second,dec,decimal,declare,default,delayed,delete,desc,describe,' .
'deterministic,distinct,distinctrow,div,double,drop,dual,each,else,elseif,enclosed,escaped,exists,exit,explain,' .
'false,fetch,float,float4,float8,for,force,foreign,from,fulltext,get,grant,group,having,high_priority,hour_microsecond,' .
'hour_minute,hour_second,if,ignore,in,index,infile,inner,inout,insensitive,insert,int,int1,int2,int3,int4,int8,' .
'integer,interval,into,io_after_gtids,io_before_gtids,is,iterate,join,key,keys,kill,leading,leave,left,like,limit,' .
'linear,lines,load,localtime,localtimestamp,lock,long,longblob,longtext,loop,low_priority,master_bind,' .
'master_ssl_verify_server_cert,match,maxvalue,mediumblob,mediumint,mediumtext,middleint,minute_microsecond,' .
'minute_second,mod,modifies,natural,nonblocking,not,no_write_to_binlog,null,numeric,on,optimize,option,optionally,' .
'or,order,out,outer,outfile,partition,precision,primary,procedure,purge,range,read,reads,read_write,real,references,' .
'regexp,release,rename,repeat,replace,require,resignal,restrict,return,revoke,right,rlike,schema,schemas,' .
'second_microsecond,select,sensitive,separator,set,show,signal,smallint,spatial,specific,sql,sqlexception,sqlstate,' .
'sqlwarning,sql_big_result,sql_calc_found_rows,sql_small_result,ssl,starting,straight_join,table,terminated,then,' .
'tinyblob,tinyint,tinytext,to,trailing,trigger,true,undo,union,unique,unlock,unsigned,update,usage,use,using,utc_date,' .
'utc_time,utc_timestamp,values,varbinary,varchar,varcharacter,varying,when,where,while,with,write,xor,year_month,' .
'zerofill,nonblocking'
);
break;
case 'pdo_sqlite':
$dboptions['reservedwords'] = explode(
',',
'abort,action,add,after,all,alter,analyze,and,as,asc,attach,autoincrement,' .
'before,begin,between,by,cascade,case,cast,check,collate,column,commit,conflict,constraint,create,cross,current_date,' .
'current_time,current_timestamp,database,default,deferrable,deferred,delete,desc,detach,distinct,drop,each,else,end,' .
'escape,except,exclusive,exists,explain,fail,for,foreign,from,full,glob,group,having,if,ignore,immediate,in,index,' .
'indexed,initially,inner,insert,instead,intersect,into,is,isnull,join,key,left,like,limit,match,natural,no,not,' .
'notnull,null,of,offset,on,or,order,outer,plan,pragma,primary,query,raise,references,regexp,reindex,release,rename,' .
'replace,restrict,right,rollback'
);
break;
case 'pdo_pgsql':
$dboptions['port'] = isset($configdb['port']) ? $configdb['port'] : '5432';
$dboptions['reservedwords'] = explode(
',',
'all,analyse,analyze,and,any,as,asc,authorization,between,bigint,binary,bit,' .
'boolean,both,case,cast,char,character,check,coalesce,collate,column,constraint,convert,create,cross,current_date,' .
'current_time,current_timestamp,current_user,dec,decimal,default,deferrable,desc,distinct,do,else,end,except,exists,' .
'extract,float,for,foreign,freeze,from,full,grant,group,having,ilike,in,initially,inner,int,integer,intersect,interval,' .
'into,is,isnull,join,leading,left,like,limit,localtime,localtimestamp,natural,nchar,new,none,not,notnull,null,nullif,' .
'numeric,off,offset,old,on,only,or,order,outer,overlaps,overlay,placing,position,primary,real,references,right,row,' .
'select,session_user,setof,similar,smallint,some,substring,table,then,time,timestamp,to,trailing,treat,trim,union,' .
'unique,user,using,varchar,verbose,when,where,false,true'
);
}
return $dboptions;
}
/**
* Utility function to determine which 'end' we're using right now. Can be either "frontend", "backend", "async" or "cli".
*
* NOTE: We retain the $_SERVER global here as this method can get called very early and the Request object might not exist yet
*
* @param string $mountpoint
* @return string
*/
public function getWhichEnd($mountpoint = '')
{
if (empty($mountpoint)) {
$mountpoint = $this->get('general/branding/path');
}
if (empty($_SERVER['REQUEST_URI'])) {
// We're probably in CLI mode.
$this->app['end'] = 'cli';
return 'cli';
}
// Set scriptname, take care of odd '/./' in the SCRIPT_NAME, which lightspeed does.
$scriptname = str_replace('/./', '/', $_SERVER['SCRIPT_NAME']);
// Get the script's filename, but _without_ REQUEST_URI. We need to str_replace the slashes, because of a
// weird quirk in dirname on windows: http://nl1.php.net/dirname#refsect1-function.dirname-notes
$scriptdirname = '#' . str_replace("\\", "/", dirname($scriptname));
$scripturi = str_replace($scriptdirname, '', '#' . $_SERVER['REQUEST_URI']);
// make sure it starts with '/', like our mountpoint.
if (empty($scripturi) || ($scripturi[0] != '/')) {
$scripturi = '/' . $scripturi;
}
// If the request URI is '/bolt' or '/async' (or starts with '/bolt/' etc.), assume backend or async.
$mountpoint = '/' . ltrim($mountpoint, '/');
if ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest')
|| $scripturi === '/async' || strpos($scripturi, '/async/') === 0) {
$end = 'async';
} elseif ($scripturi === $mountpoint || strpos($scripturi, $mountpoint . '/') === 0) {
$end = 'backend';
} else {
$end = 'frontend';
}
$this->app['end'] = $end;
return $end;
}
/**
* Get a timestamp, corrected to the timezone.
*
* @return string timestamp
*/
public function getTimestamp($when)
{
$timezone = $this->get('general/timezone');
$now = date_format(new \DateTime($when, new \DateTimeZone($timezone)), 'Y-m-d H:i:s');
return $now;
}
/**
* Get the current timestamp, corrected to the timezone.
*
* @return string current timestamp
*/
public function getCurrentTimestamp()
{
$timezone = $this->get('general/timezone');
$now = date_format(new \DateTime($timezone), 'Y-m-d H:i:s');
return $now;
}
/**
* Add error to flash if session exists
* @param $error
*/
protected function flashError($error)
{
if (!isset($this->app['session'])) {
return;
}
/** @var \Symfony\Component\HttpFoundation\Session\Session $session */
$session = $this->app['session'];
if (gettype($session) !== 'object') {
return;
}
$session->getFlashBag()->set('error', $error);
}
}